C#网络编程

HTTP 协议

作者:陈广 日期:2018-11-10


HTTP 协议是 Web 浏览器和 Web 服务器之间的通信协议。当今的 Web 开发技术百花齐放,各种前端框架层出不穷。甚至于 Web 开发技术还逐渐入侵传统的桌面应用和手机端 App 应用,部分桌面应用和手机 App 应用在浏览器外包个壳,也能象原生程序一样运行。所有这些技术追根溯源,它们的底层基础都离不开 HTTP 协议。理解 HTTP 协议,能帮助你在将来的学习或技术转换的过程中事半功倍。

HTTP 协议介绍

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。它允许将超文本标记语言(HTML)文档从 Web 服务器传送到客户端的浏览器。目前我们使用的 HTTP 协议是 HTTP/1.1 版本。

1960年美国人Ted Nelson(泰德·纳尔逊) 构思了一种通过计算机处理文本信息的方法,并称之为超文本(HyperText),这成为了 HTTP 超文本传输协议标准架构的发展根基。Ted Nelson 组织协调万维网协会(World Wide Web Consortium)和 Internet 工作小组(Internet Engineering Task Force)共同合作研究,最终发布了一系列的 RFC,其中最著名的就是 RFC 2616。RFC 2616 定义了 HTTP 协议的我们今天普遍使用的一个版本 —— HTTP 1.1。

图 1: Ted Nelson(泰德·纳尔逊)

1989年,同时在 CERN(欧洲核子研究中心)任职期间,麻省理工学院调查员 Tim Berners-Lee(蒂姆·伯纳斯·李)提出了万维网(World Wide Web,WWW)的初步构想,并发明了很多支持万维网的集成工具,包括:统一资源标识符(URI)、超文本传输协议(HTTP)、网页浏览器、超文本标记语言(HTML)等等。1990年,他设计制作出世界上第一个网页浏览器。1990年12月25日,他成功利用互联网实现了HTTP客户端与服务器的第一次通讯,并获得由美国计算机协会(ACM)颁发的 2016 年度图灵奖。

图 2: Tim Berners-Lee(蒂姆·伯纳斯·李)

HTTP 协议工作原理

我们打开浏览器,在地址栏输入 URL,按回车,然后就看到了网页。这是什么原理呢?实际上我们输入 URL 后,浏览器就给 Web 服务器发送了一个 HTTP 请求(HTTP Request),Web 服务器接到 HTTP 请求后进行处理,生成相应的 HTTP 响应(HTTP Response),然后发送给浏览器。浏览器解析 HTTP 响应中的 HTML,这样我们就看到了网页。该过程如图3所示。

图 3:HTTP 协议工作原理

HTTP 是一个无状态的协议。无状态是指客户机(Web浏览器)和服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后服务器返回响应,连接就被关闭了,在服务器端不保留连接的有关信息。HTTP 遵循 请求/响应 模型。客户机(浏览器)向服务器发送请求,服务器处理请求并返回适当的响应。所有 HTTP 连接都被构造成一套请求和响应。

通过上面的介绍,我们已经了解了 HTTP 协议的大概的工作原理,那么 HTTP 请求和 HTTP 响应的数据包(报文)具体有哪些内容呢?要深入了解它们,我们可以借助于一个强大的 Web 调试工具:Fiddler。

抓包工具 Fiddler

Fiddler 是用 C# 开发的,作者是 Eric Lawrence,是个大师级人物,曾经在微软总部西雅图工作。Fiddler 用途非常广泛,能记录所有客户端和服务器的 HTTP 和 HTTPS 请求,允许你监视、设置断点,甚至修改输入输出数据。

Fiddler 的官方网站是: https://www.telerik.com/fiddler,下载地址是:https://www.telerik.com/download/fiddler

首先安装 Fiddler,过程省略,我们准备用它来抓取一个最简单的页面。

抓取一个简单的静态页面

在这一节中,我们首先创建一个最简单的页面,然后装上一个简单的 IIS Web 服务器,然后访问页面,通过 Fiddler 抓取访问过程中的请求和响应数据包来分析 HTTP 协议中的最基本内容。

创建一个简单页面

首先打开 Visual Studio Code,创建一个 index.html 文件,在文件开始处输入!,此时会弹出提示,按回车键自动生成一段代码,然后我们在<body>标签内输入hello World!

最终代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    hello World!
</body>
</html>

安装 IIS Web 服务器

要通过 HTTP 协议访问我们创建的页面需要架设一个 Web 服务器。你可以买一个阿里或腾讯服务器架设、在自己的电脑上安装 Linux 或 Windows Server 虚拟机架设,这些都不太方便。最省事的方法是在 vscode 内安装一个 IIS 扩展。

请参照下图在 vscode 的侧栏选中【扩展】按钮,然后在搜索栏中输入 IIS,然后在搜索出的扩展中选中 【IIS Exprerss】项,单击下面的小按钮【Install】进行安装。

图 4:安装 IIS Express 扩展

安装完成后,【Install】按钮会变成【Reload】按钮,单击它加载以激活扩展。

接下来,在 vscode 中按下【Shift + Ctrl + p】快捷键,打开【命令面板】,输入 iis,如下图所示,出现三条命令。

图 5:IIS Express 命令

三条命令为:

  • IIS Express:Start Website:用于启动 IIS
  • IIS Express:Stop Website:用于停止 IIS
  • IIS Express:Restart Website:用于重启 IIS

我们在当前页面为 index.html 的情况下选中第一条命令,启动 IIS。此时 IIS 启动,并自动打开浏览器访问 index.html 页面。可以观察到浏览器地址栏 URL 为:http://localhost:10960/

使用 Fiddler 抓取数据包

接下来我们打开 Fiddler 来抓取新建网页的访问数据。启动 Fiddler,然后在 IIS 启动的情况下打开浏览器访问之前观察到的 URL(本例为 http://localhost:10960/)。然后在 Fiddler 左侧的 Session 列表中根据浏览器 URL 找到相应的项,并选中它。在 Fiddler 右侧上方功能面板上选中【Inspectors】选项卡以及【Raw】选项卡。然后在下方空格功能面板上选中【Raw】选项卡。最终效果如下图所示:

图 6:使用 Fiddler 抓取数据包

注意:按以上方法抓数据包往往不成功,因为第二次访问同一页面会调用本地缓存。此时可以将 index.html 文件稍作修改,如在Hello World!后面加一个句号,然后再查看抓到的数据包,就没问题了。另外,打开 Fiddler 后,会抓取到大量数据包,让你难于找到你想要的数据包。此时可以在 Session 列表上点鼠标右键,在弹出菜单中选择【Remove】➤【All Sessions】将所有会话删除,然后再用浏览器访问页面。另外还可以单击 Fiddler 左下角【Capturing】选项卡,暂停抓包,等到准备抓包时再点击它启动抓包。抓到想要的包后,再次点击暂停抓包。这样 Session 列表就会比较干净了。

通过图6可以看到,右侧上方窗格显示的抓取到的浏览器 HTTP 请求报文,右侧下方空格显示的是服务器返回的响应报文。下面我们通过这两段报文来初步认识 HTTP 协议。

HTTP 请求报文结构

先上图,有了这张图我们就可以很清楚看到请求报文的每一块内容是什么了。

图 7:请求报文结构

起始行

先来看看起始行的内容:

GET http://localhost:10960/ HTTP/1.1

起始行分为三修正部分:方法、URL、协议版本。第一部分方法在之前翻译的 ASP.NET Core 书里又称为动词,表示请求的方法。HTTP 1.1 版本里有8个,都用大写表示:

方法

  • GET:用于获取资源,常用于向服务器查询某些信息。
  • POST:用于将表单中的数据发送到服务器
  • HEAD:获取响应报文头,响应端收到 HEAD 请求后,只会返回相应的响应头,不会返回响应体。
  • PUT:上传文件
  • DELETE:删除文件
  • TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
  • CONNECT:要求用隧道协议连接代理
  • OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求

其实最常用的方法就是 GET 和 POST 两个,这两个方法需要在后面重点研究。

URL 和 URI

起始行的第二部分是 URL,严格上来说应该为 URI。这两个概念很容易混淆,两者有什么区别呢?

URL 的全称是 Uniform Resource Locator,中文译名为“统一资源定位符”,就是我们在浏览器地址栏看到的地址,用于完整地描述 Internet 上某一处资源的地址。Internet 上的每个网页都有一个标识,一般称之为 URL 地址,或 Web 地址,俗称“网址”。URL 地址可以是本地磁盘,也可以是局域网上某一台计算机,更多的是 Internet 上的站点。

URI 的全称是 Uniform Resource Identifier,中文译名为“统一资源标识符”,用来唯一地标识一个资源。

URI 和 URL 的区别有点类似对象的区别。URI 是一个抽象概念,它是标识网络资源的方法,URL 就是一种具体的方法了。打个比方,我们要标识一个人的身份,可以使用身份证、手机号码、银行帐号(现在网上实名认证都是通过手机和银行帐号来实现的),还可以通过一个具体的通信地址来标识一个人,如 xx省/xx市/xx街/xx门牌号/xx栋/xx号房/姓名。这个地址是不是很象我们看到的 URL 啊?没错,URL 就是通过地址的方式标识一个网络上的资源。它是 URI 的具体实现方式之一。我们可以把 URL 称之为 URI,但不能把 URI 称之为 URL。

URL 格式

URL 的基本格式如下:

schema://host[:port#]/path/.../[?query-string][#anchor]

以下是每一部分的解释:

  • schema:指定使用的协议(如:http,https,ftp)。
  • host:HTTP 服务器的 IP 地址或域名。
  • port#:HTTP 服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,则必须指明。如 http://localhost:10960/ 。
  • path:访问资源的路径。
  • query-string:查询字符串,用于存放发送给服务器的数据。
  • anchor:锚点

其中锚点(Anchor)是一种超链接,只是它是页面内部的超链接。假如有一个网页很长,而且里面的内容可以分为多个部分。我们就可以在网页顶部设置一个锚点,以便浏览者单击相应锚点,快速到达本页内相应的位置,而不必在一个很长的页面里自行寻找。锚点在 URL 的最右边,前面有一个字符#,如#stuff

URL 的一个例子如下:

http://www.iotxfd.cn/article/Pro-ASP.NET-Core-MVC/?name=Routing&x=true#stuff

其中:

  • schema(协议):http
  • host(域名):www.iotxfd.cn
  • path(资源的路径):/article/Pro-ASP.NET-Core-MVC
  • query-string(参数):?name=Routing&x=true
  • anchor(锚点):stuff

协议版本

起始行第三部分是协议版本,各版本介绍如下:

  • HTTP/0.9 :只接受GET一种请求方法,没有在通信中指定版本号,且不支持请求头。由于该版本不支持 POST 方法,因此客户端无法向服务器传递太多信息。
  • HTTP/1.0 :第一个在通信中指定的版本号,至今被广泛采用,特别是在代理服务器中。
  • HTTP/1.1 :当前正在使用的版本号,持久连接被默认采用,并能很好地配合代理服务器工作。还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。
  • HTTP/2.0 正在开发中······

首部(header)

HTTP 请求报文的第二部分是 header。header 是客户向服务器发送信息时的附加信息,数量为0到多个,每行代表一个 header,格式为 header名 + 一个冒号 + 一个空格 + 首部值。每个 header 都有特殊的作用。

图7中 header 部分报文如下:

Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept-Language: zh-CN
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
If-Modified-Since: Wed, 07 Nov 2018 02:51:32 GMT
If-None-Match: "fefd54ca4476d41:0"
Host: localhost:10960
Connection: Keep-Alive

后面用到某个 header 时会进行详细介绍,现在只粗略介绍我们看到的 header:

  • Cache-Control :用于随报文传送缓存指示
  • User-Agent :浏览器用来告诉服务器,客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。如果我们想模拟各种不同的客户端,只需修改 User-Agent 就可以伪装成各种客户端。
  • Accept-Language :作用是浏览器声明自己接受的语言。
  • Accept :表示浏览器客户端可以接受的媒体类型。如果Accept: text/html代表浏览器可以接受服务器返回 html 文档。
  • Upgrade-Insecure-Requests :让浏览器不再显示 https 页面中的 http 请求警报
  • Accept-Encoding :此 header 跟压缩有关,Accept-Encoding: gzip, deflate是告诉服务器浏览器支持 gzip 压缩。
  • If-Modified-Since :此 header 用于向服务器发送一个时间,如果服务器资源在此时间后没有更改过,则返回状态码 304,客户端接到之后,就直接把本地缓存文件显示到浏览器中,这样就节省了传输数据量。
  • If-None-Match :也跟缓存有关,服务器会给某资源生成一个哈希码,浏览器请求时会将此哈希码一起返回给浏览器,当浏览器再次请求该资源时,会连同哈希码一起发送到服务器。如果资源的哈希码没变,说明资源没有改变,则服务器返回状态码 304,客户端接到之后,则直接使用该资源在本地的缓存。
  • Host :给出接收请求的服务器的主机名和端口号
  • Connection :值为Keep-Alive表示开启 HTTP 持久连接,这是 HTTP 1.1 的默认值;值为Close时关闭 HTTP 持久连接。HTTP协议是基于 TCP 协议的。当一个网页完全打开后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭;如果客户端再次访问这个服务器上的网页,将会继续使用这条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)中设定这个时间。

Fiddler 中的【Headers】选项卡将 header 进行了分类,以方便我们查看。单击上方窗格【Headers】选项卡,显示内容如下图所示:

图 8:请求报文结构

HTTP 响应报文结构

先上响应报文结构图

图 9:响应报文结构

响应报文和请求报文结构类似,只是此图多了一个 Body 部分,其实请求报文也是有 Body 部分的,只是之前的请求没用到而已。

起始行

先看看起始行的内容:

HTTP/1.1 200 OK

也是分为三部分:协议版本、状态码、状态消息。协议版本我们在请求报文中介绍了,下面介绍状态码和状态消息。

状态码

每个 HTTP 响应报文都会携带一个 HTTP 状态码(HTTP Status Code),用于告诉客户端请求是否成功。状态码是一个 3 位数字的代码。其作用是 Web 服务器用来告诉客户端发生了什么事。

HTTP/1.1 中定义了5类状态码,第一个数字定义了响应的类别。HTTP 状态码被分为5大类,支持如下表所示的状态码:

状态码 已定义的范围 分类
1XX 100 ~ 101 信息提示,表示请求已被成功接收,继续处理
2XX 200 ~ 206 成功,表示请求已被成功接收、理解、接受
3XX 300 ~ 305 重定向,要完成请求,必须进行更进一步的处理
4XX 400 ~ 415 客户端错误,请求有语法错误或请求无法实现
5XX 500 ~ 505 服务器错误,服务器未能实现合法的请求

如果看到一个状态码 518,不知道是什么意思,这时候只要知道 518 属于 5XX 服务器错误就可以了。

下面抽一些常见状态码来讲解

200(OK)

最常见的状态码就是成功响应状态码 200 了,它表明该请求被成功地完成,所请求的资源成功地发送回客户端。

204(No Content,没有内容)

返回的 HTTP 响应只有一些 Header 和一个状态行,没有实体(body)内容。204状态码作用如下:

  1. 在不获取资源的情况下了解资源情况(比如判断其类型)。
  2. 通过查看 HTTP 响应中的状态码看某个对象是否存在。
  3. 通过查看 Header 测试资源是否被修改。
206(Partial Content,部分内容)

206 状态码代表服务器已经成功处理了部分 GET 请求(只有发送 GET 方法的 HTTP 请求,Web 服务器才可能返回 206)。

206 的应用场景如下。

  1. FlashGet、迅雷或者 HTTP 下载工具都是使用 206 状态码来实现断点续传的。
  2. 将一个大文档分解为多个下载段同时下载,比如在线看视频。
301(Moved Permanently)

服务器返回 301 的时候,表示请求的网页已经永久性地转移到另一个地址。

在如下情况需要用到 301。

  1. 防止用户输错域名。比如 Google 担心用户输错域名,就把其他类似的域名买下来,比如 go0gle.com,然后重定向到 www.google.com。
  2. 网站更换域名。一些网站壮大后,会换个更好的域名。比如京东以前的域名是 www.360buy.com,现在的域名是 www.jd.com。
  3. 有多个权重不错的域名,需要把所有的权重都传递到新域名上,这就需要 301 重定向了。如果不设置 301,多个域名绑定在一个主机头上,会被搜索引擎认为是两个相同的站点,不利于网站的排名。绑定的域名越多,内容重复度也就越高,排名越低。
302(Found)

当我们访问一个 URL 的时候,服务器要我们访问另一个资源,这时候浏览器会继续发一个 HTTP,请求访问新的资源。

状态码 301 和 302 在语法上是一模一样的,都是在 Location 中返回新的 URL。两者的区别在于:

  1. 301 表示旧地址的资源已经被永久地移除了(这个资源不可访问了),搜索引擎会把权重算到新地址:
  2. 302 表示旧地址的资源还在(仍然可以访问),这个重定向只是临时地从旧地址跳转到新地址,搜索引擎会把权重算到旧地址。
304(Not Modified)

304 状态码代表上次的文档已经被缓存了,还可以继续使用。如果你不想使用缓存,可以用【Ctrl + F5】组合键强制刷新页面。

400(Bad Request)

状态码 400 表示客户端请求有语法错误,发送的 HTTP 请求中的数据有错误(如表单有错误、Cookie 有错误)。不能被服务器所理解。

401(Unauthorized)

状态码 401 是指未授权错误。有些网页采用的是 HTTP 基本认证(Basic Authentication),需要在 HTTP 请求中带上 Authorization Header,否则服务器会返回状态码 401。

403(Forbidden)

403 状态码表示 Web 客户端发送的请求被 Web 服务器拒绝了。如果服务器想说明为什么拒绝请求,可以在 Body 中描述原因。但这个状态码通常表示服务器不想说明拒绝原因。

404(Not Found)

当你访问一个 URL,这个 URL 的域名是正确的,但是资源不存在,服务器就会返回 404 状态码,告诉浏览器资源不存在(意味着输错了 URL)。

状态码 500 代表服务器内部错误。出现错误的原因有很多,比如代码的错误、数据库连接语句出错、程序内部抛出异常、空指针错误等。

503(Server Unavailable)

状态码 503 表示服务器暂时不可用。由于服务器维护或者过载,服务器当前无法处理请求;这个状况是临时的,并且将在一段时间以后恢复。

首部(header)

HTTP 响应报文的第二部分是首部(Header),结构和请示报文首部完全一样。

图7中 header 部分报文如下:

Content-Type: text/html
Last-Modified: Wed, 07 Nov 2018 03:10:26 GMT
Accept-Ranges: bytes
ETag: "5aee686e4776d41:0"
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?YzpcVXNlcnNcY2dcRGVza3RvcFx3ZWJcaW5kZXguaHRtbA==?=
X-Powered-By: ASP.NET
Date: Wed, 07 Nov 2018 03:10:37 GMT
Content-Length: 286

接下来介绍我们看到的 header:

  • Content-Type :,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件。
  • Last-Modified :此 header 与请求报文中的If-Modified-Since header 配合使用。在浏览器第一次请求某一个URL时,服务器端的返回状态会是 200,内容是客户端请求的资源,同时有一个Last-Modified的属性标记此文件在服务器端最后被修改的时间。客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since header,询问该时间之后文件是否有被修改过。如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed.)状态码,内容为空,浏览器从本地缓存读取资源,这样就节省了传输数据量。
  • Accept-Ranges :Web 服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,并且单位为字节。none:表示不接受。
  • ETag :是实体标签(Entity Tag)的缩写,与请求报文中的If-None-Match配合使用。它是根据资源生成的一个哈希值,当资源被更改时,会重新生成此哈希值,浏览器在第二次请求资源时会将第一次请求所收到的哈希值通过If-None-Match传递给服务器与新值进行对比,决定是否使用本地缓存。
  • Server :Web 服务器表明自己是什么软件及版本等信息。
  • X-SourceFiles :以X为前缀的 header 表示它是扩展 header,并非 HTTP 标准。上述值表示它是 IIS / IIS Express 中的某些调试模块可以理解的 header。它包含到磁盘上源文件的 base 64 编码路径,并用于将页面生成的输出链接回该源文件。它只为 localhost 主机请求生成,所以将应用程序部署到实际服务器时,它不会出现。
  • X-Powered-By :告诉客户端处理请求和响应的是什么引擎。
  • Date :提供日期和时间标志,说明报文是什么时间创建的。
  • Content-Length :Body 的长度。

主体(Body)

HTTP 响应报文的第三部分是主体(Body),Header 和 Body 之间有一个空行。以下是 Body 中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    hello World!
</body>
</html>

这个大家一看就懂了,是我们在 index.html 中输入的代码。浏览器通过解析这些 HTML 代码来显示相应的内容。

网页的打开过程

接下来,我们往 index.html 文件里加入 CSS、JavaScript及图片文件,看看网页的打开过程。

在 index.html 文件所在的目录下新建一个 style.css 文件以及 index.js 文件,然后随便找一张图片也放到此文件夹下,我这里就放一张大神的图片,名为 Anders.png。

style.css 文件输入如下代码:

body{
    background-color: cornflowerblue;
}

index.js 文件输入如下代码:

//这是一个 JavaScript 文件

更改 index.cshtml 文件的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <img src="Anders.png" alt="Delphi/C#之父:Anders Hejlsberg">
</body>
<script src="index.js"></script>
</html>

现在,再次用浏览器通过 URL http://localhost:10960/index.html 打开此网页,记得打开 Fiddler 抓包。

先看下运行效果,一睹大神的帅气照片:

图 10:运行效果

接下来是 Fiddler 的抓包结果:

图 11:Fiddler 抓包结果

如上图所示,我们看到 Session 列表中一共抓了5个包,上图右边窗格所显示的是第一个包,对 index.html 文件的请求及响应,这在上一节中已介绍过。

请求图片

在浏览器收到服务器发送过来的 HTML 文件后,在进行解析的过程中,发现用到图片,然后再次向服务器发出请求以获取图片,请求报文如下:

GET http://localhost:10960/Anders.png HTTP/1.1
Referer: http://localhost:10960/index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept: image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
Accept-Language: zh-CN
Accept-Encoding: gzip, deflate
Host: localhost:10960
Connection: Keep-Alive
Pragma: no-cache

可以看到,起始行中第二部分为图片 URL 地址。Referer header 告诉服务器是哪个页面发出的请求。

响应报文如下可以通过【ImageView】选项卡来查看,如下图所示

图 12:请求图片

请求 JavaScript 文件

接下来就是请求 HTML 文件中引用到的 JavaScript 文件,请求报文如下:

GET http://localhost:10960/index.js HTTP/1.1
Referer: http://localhost:10960/index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept: */*
Accept-Language: zh-CN
Accept-Encoding: gzip, deflate
Host: localhost:10960
Connection: Keep-Alive
Pragma: no-cache

响应报文如下:

HTTP/1.1 200 OK
Content-Type: application/javascript
Last-Modified: Sat, 10 Nov 2018 00:15:32 GMT
Accept-Ranges: bytes
ETag: "f3696b7e8a78d41:0"
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?YzpcVXNlcnNcY2dcRGVza3RvcFx3ZWJcaW5kZXguanM=?=
X-Powered-By: ASP.NET
Date: Sat, 10 Nov 2018 00:55:06 GMT
Content-Length: 32

可以观察到,请求和响应过程与 HTML 文件的请求与响应基本相同,JavaScript 文件的正文也是放在 Body 中的。

请求 CSS 文件

接下来就是请求 HTML 文件中引用到的 CSS 文件,请求报文如下:

GET http://localhost:10960/style.css HTTP/1.1
Referer: http://localhost:10960/index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134
Accept: text/css,*/*;q=0.1
Accept-Language: zh-CN
Accept-Encoding: gzip, deflate
Host: localhost:10960
Connection: Keep-Alive
Pragma: no-cache

响应报文如下:

HTTP/1.1 200 OK
Content-Type: text/css
Last-Modified: Sat, 10 Nov 2018 00:09:19 GMT
Accept-Ranges: bytes
ETag: "a73e7ba08978d41:0"
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?YzpcVXNlcnNcY2dcRGVza3RvcFx3ZWJcc3R5bGUuY3Nz?=
X-Powered-By: ASP.NET
Date: Sat, 10 Nov 2018 00:55:06 GMT
Content-Length: 47

body{
    background-color: cornflowerblue;
}

CSS 文件也是被放在 Body 中。

断开连接

最后一个数据包的响应报文如下:

HTTP/1.0 200 Connection Established
FiddlerGateway: Direct
StartTime: 08:55:06.594
Connection: close

可以看到最后一项Connection: close,表示连接断开。现在我们可以深刻理解,为何 HTTP 1.1 版本要改为持久连接了,往往一个 HTML 页面里面会包含大量的图片及其它文件,而这每一个文件都要单独向服务器发送请求报文进行请求,如果每一次请求都要重新向服务器申请连接,会大量浪费服务器资源。 持久连接后,每一个请求都可以使用相同的链接,在一个页面的所有请求完成后再关闭链接则可以节省大量服务器资源。

;

© 2018 - IOT小分队文章发布系统 v0.3